﻿using System;
using DependencyAnalysis;
using DependencyAnalysis.DataModel;
using DependencyAnalysis.Internal;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTests
{
    [TestClass]
    public class VerifyAccessTypeTest
    {
        private IVerifier _verifier;
        private IDependencyModel _dependencyModel;

        [TestInitialize]
        public void Initialize()
        {
            _dependencyModel = new DependencyModel();
            var descriptor = DependencyModelTest.BuildDependencyDescriptor(1, 2, true, false);
            descriptor.Target.ImplementingType.AbstractionType = AbstractionType.Abstract;
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(6, 7, true, false);
            descriptor.Target.Identifier = "Module7.Submodule7.Abstract7.Member";
            descriptor.Target.ImplementingType.Identifier = "Module7.Submodule7.Abstract7";
            descriptor.Target.ImplementingType.AbstractionType = AbstractionType.Abstract;
            descriptor.Identifier = "7 @ 6 abstract";
            _dependencyModel.Add(descriptor); descriptor = DependencyModelTest.BuildDependencyDescriptor(6, 7, true, false);
            descriptor.Target.Identifier = "Module7.Submodule7.Interface7.Member";
            descriptor.Target.ImplementingType.Identifier = "Module7.Submodule7.Interface7";
            descriptor.Target.ImplementingType.AbstractionType = AbstractionType.Interface;
            descriptor.Identifier = "7 @ 6 interface";
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(8, 9, true, false);
            descriptor.Target.ImplementingType.AbstractionType = AbstractionType.Interface;
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 5, true, false);
            descriptor.Target.ImplementingType.AbstractionType = AbstractionType.Concrete;
            _dependencyModel.Add(descriptor);
            _verifier = new Verifier(_dependencyModel);

            descriptor = DependencyModelTest.BuildDependencyDescriptor(10, 11, true, false);
            descriptor.Target.Identifier = "Module10.Submodule10.Abstract10.Member";
            descriptor.Target.ImplementingType.Identifier = "Module10.Submodule10.Abstract10";
            descriptor.Target.ImplementingType.AbstractionType = AbstractionType.Abstract;
            descriptor.Identifier = "11 @ 10 abstract";
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(10, 11, true, false);
            descriptor.Target.Identifier = "Module10.Submodule10.Interface10.Member";
            descriptor.Target.ImplementingType.Identifier = "Module10.Submodule10.Interface10";
            descriptor.Target.ImplementingType.AbstractionType = AbstractionType.Interface;
            descriptor.Identifier = "11 @ 10 interface";
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(10, 11, true, false);
            descriptor.Target.Identifier = "Module10.Submodule10.Concrete10.Member";
            descriptor.Target.ImplementingType.Identifier = "Module10.Concrete10.Interface10";
            descriptor.Target.ImplementingType.AbstractionType = AbstractionType.Concrete;
            descriptor.Identifier = "11 @ 10 concrete";
            _dependencyModel.Add(descriptor);
        }

        [TestMethod]
        public void WhenTargetIsNullThrowArgumentNullException()
        {
            Assert1.Throws<ArgumentNullException>(() => _verifier.VerifyAccessType<TypeDescriptor>(new TypeDescriptor(), null, AbstractionType.Abstract));
        }

        [TestMethod]
        public void WhenReferrerIsNullThrowArgumentNullException()
        {
            Assert1.Throws<ArgumentNullException>(() => _verifier.VerifyAccessType(null, new TypeDescriptor(), AbstractionType.Abstract));
        }

        [TestMethod]
        public void WhenAccessTypeMatchesAbstractBetweenTypeAndSubmoduleNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module1.Submodule1.Type1" },
                new SubmoduleDescriptor { Identifier = "Module2.Submodule2" }, AbstractionType.Abstract));
        }

        [TestMethod]
        public void WhenAccessTypeMatchesInterfaceBetweenTypeAndSubmoduleNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module8.Submodule8.Type8" },
                new SubmoduleDescriptor { Identifier = "Module9.Submodule9" }, AbstractionType.Interface));
        }

        [TestMethod]
        public void WhenAccessTypeMatchesConcreteBetweenTypeAndSubmoduleNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module4.Submodule4.Type4" },
                new SubmoduleDescriptor { Identifier = "Module5.Submodule5" }, AbstractionType.Concrete));
        }

        [TestMethod]
        public void WhenAccessTypeMatchesAbstractOrInterfaceBetweenTypeAndSubmoduleNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module1.Submodule1.Type1" },
                new SubmoduleDescriptor { Identifier = "Module2.Submodule2" }, AbstractionType.Abstract | AbstractionType.Interface));
        }

        [TestMethod]
        public void WhenSubmoduleIsNotFoundNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module3.Submodule3.Type3" },
                new SubmoduleDescriptor { Identifier = "Dummy.NotFound" }, AbstractionType.Abstract));
        }

        [TestMethod]
        public void WhenAccessTypeNotMatchesAbstractBetweenTypeAndSubmoduleThrowDependencyException()
        {
            Assert1.Throws<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module8.Submodule8.Type8" },
                new SubmoduleDescriptor { Identifier = "Module9.Submodule9" }, AbstractionType.Abstract));
        }

        [TestMethod]
        public void WhenAccessTypeNotMatchesInterfaceBetweenTypeAndSubmoduleThrowDependencyException()
        {
            Assert1.Throws<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module1.Submodule1.Type1" },
                new SubmoduleDescriptor { Identifier = "Module2.Submodule2" }, AbstractionType.Interface));
        }

        [TestMethod]
        public void WhenAccessTypeNotMatchesConcreteBetweenTypeAndSubmoduleThrowDependencyException()
        {
            Assert1.Throws<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module8.Submodule8.Type8" },
                new SubmoduleDescriptor { Identifier = "Module9.Submodule9" }, AbstractionType.Concrete));
        }

        [TestMethod]
        public void WhenAccessTypeMatchesAbstractBetweenTypeAndModuleNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module1.Submodule1.Type1" },
                new ModuleDescriptor { Identifier = "Module2" }, AbstractionType.Abstract));
        }

        [TestMethod]
        public void WhenAccessTypeMatchesInterfaceBetweenTypeAndModuleNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module8.Submodule8.Type8" },
                new ModuleDescriptor { Identifier = "Module9" }, AbstractionType.Interface));
        }

        [TestMethod]
        public void WhenAccessTypeMatchesConcreteBetweenTypeAndModuleNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module4.Submodule4.Type4" },
                new ModuleDescriptor { Identifier = "Module5" }, AbstractionType.Concrete));
        }

        [TestMethod]
        public void WhenAccessTypeMatchesAbstractOrInterfaceBetweenTypeAndModuleNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module1.Submodule1.Type1" },
                new ModuleDescriptor { Identifier = "Module2" }, AbstractionType.Abstract | AbstractionType.Interface));
        }

        [TestMethod]
        public void WhenModuleIsNotFoundNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module3.Submodule3.Type3" },
                new ModuleDescriptor { Identifier = "NotFound" }, AbstractionType.Abstract));
        }

        [TestMethod]
        public void WhenAccessTypeNotMatchesAbstractBetweenTypeAndModuleThrowDependencyException()
        {
            Assert1.Throws<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module8.Submodule8.Type8" },
                new ModuleDescriptor { Identifier = "Module9" }, AbstractionType.Abstract));
        }

        [TestMethod]
        public void WhenAccessTypeNotMatchesInterfaceBetweenTypeAndModuleThrowDependencyException()
        {
            Assert1.Throws<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module1.Submodule1.Type1" },
                new ModuleDescriptor { Identifier = "Module2" }, AbstractionType.Interface));
        }

        [TestMethod]
        public void WhenAccessTypeNotMatchesConcreteBetweenTypeAndModuleThrowDependencyException()
        {
            Assert1.Throws<DependencyException>(() =>
                _verifier.VerifyAccessType(new TypeDescriptor { Identifier = "Module8.Submodule8.Type8" },
                new ModuleDescriptor { Identifier = "Module9" }, AbstractionType.Concrete));
        }

        [TestMethod]
        public void WhenAccessTypeMatchesAbstractBetweenSubmoduleAndModuleNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new SubmoduleDescriptor { Identifier = "Module1.Submodule1" },
                new ModuleDescriptor { Identifier = "Module2" }, AbstractionType.Abstract));
        }

        [TestMethod]
        public void WhenAccessTypeMatchesInterfaceBetweenSubmoduleAndModuleNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new SubmoduleDescriptor { Identifier = "Module8.Submodule8" },
                new ModuleDescriptor { Identifier = "Module9" }, AbstractionType.Interface));
        }

        [TestMethod]
        public void WhenAccessTypeMatchesConcreteBetweenSubmoduleAndModuleNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new SubmoduleDescriptor { Identifier = "Module4.Submodule4" },
                new ModuleDescriptor { Identifier = "Module5" }, AbstractionType.Concrete));
        }

        [TestMethod]
        public void WhenAccessTypeMatchesAbstractOrInterfaceBetweenSubmoduleAndModuleNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() =>
                _verifier.VerifyAccessType(new SubmoduleDescriptor { Identifier = "Module1.Submodule1" },
                new ModuleDescriptor { Identifier = "Module2" }, AbstractionType.Abstract | AbstractionType.Interface));
        }

        [TestMethod]
        public void WhenAccessTypeNotMatchesAbstractBetweenSubmoduleAndModuleThrowDependencyException()
        {
            Assert1.Throws<DependencyException>(() =>
                _verifier.VerifyAccessType(new SubmoduleDescriptor { Identifier = "Module8.Submodule8" },
                new ModuleDescriptor { Identifier = "Module9" }, AbstractionType.Abstract));
        }

        [TestMethod]
        public void WhenAccessTypeNotMatchesInterfaceBetweenSubmoduleAndModuleThrowDependencyException()
        {
            Assert1.Throws<DependencyException>(() =>
                _verifier.VerifyAccessType(new SubmoduleDescriptor { Identifier = "Module1.Submodule1" },
                new ModuleDescriptor { Identifier = "Module2" }, AbstractionType.Interface));
        }

        [TestMethod]
        public void WhenAccessTypeNotMatchesConcreteBetweenSubmoduleAndModuleThrowDependencyException()
        {
            Assert1.Throws<DependencyException>(() =>
                _verifier.VerifyAccessType(new SubmoduleDescriptor { Identifier = "Module8.Submodule8" },
                new ModuleDescriptor { Identifier = "Module9" }, AbstractionType.Concrete));
        }

        [TestMethod]
        public void WhenAccessOfAtLeastOneMemberNotMatchesAbstractBetweenSubmoduleAndModuleThrowDependencyException()
        {
            Assert1.Throws<DependencyException>(() =>
                _verifier.VerifyAccessType(new SubmoduleDescriptor { Identifier = "Module10.Submodule10" },
                new ModuleDescriptor { Identifier = "Module11" }, AbstractionType.Abstract));
        }

        [TestMethod]
        public void WhenAccessOfAtLeastOneMemberNotMatchesInterfaceBetweenSubmoduleAndModuleThrowDependencyException()
        {
            Assert1.Throws<DependencyException>(() =>
                _verifier.VerifyAccessType(new SubmoduleDescriptor { Identifier = "Module10.Submodule10" },
                new ModuleDescriptor { Identifier = "Module11" }, AbstractionType.Interface));
        }

        [TestMethod]
        public void WhenAccessOfAtLeastOneMemberNotMatchesConcreteBetweenSubmoduleAndModuleThrowDependencyException()
        {
            Assert1.Throws<DependencyException>(() =>
                _verifier.VerifyAccessType(new SubmoduleDescriptor { Identifier = "Module10.Submodule10" },
                new ModuleDescriptor { Identifier = "Module11" }, AbstractionType.Concrete));
        }
    }
}
